import hashlib
import hmac
import json
import time
import uuid
from decimal import Decimal
from typing import Dict, Any, Optional
import aiohttp
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
import base64

from .payment_base import (
    BasePaymentService, PaymentProvider, PaymentRequest, PaymentResponse,
    PaymentNotification, OrderStatus, PaymentMethod
)
from app.core.payment_logger import payment_logger, PaymentLogLevel, PaymentEventType

class WeChatPayService(BasePaymentService):
    """微信支付服务实现"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.app_id = config.get("app_id")
        self.mch_id = config.get("mch_id")  # 商户号
        self.api_key = config.get("api_key")  # API密钥
        self.cert_path = config.get("cert_path")  # 证书路径
        self.key_path = config.get("key_path")   # 私钥路径
        self.notify_url = config.get("notify_url", "")
        
        # API URLs
        self.base_url = "https://api.mch.weixin.qq.com"
        self.unifiedorder_url = f"{self.base_url}/pay/unifiedorder"
        self.orderquery_url = f"{self.base_url}/pay/orderquery"
        self.refund_url = f"{self.base_url}/secapi/pay/refund"
    
    def get_provider_name(self) -> PaymentProvider:
        return PaymentProvider.WECHAT_PAY
    
    def get_supported_methods(self) -> list[PaymentMethod]:
        return [PaymentMethod.APP, PaymentMethod.JSAPI, PaymentMethod.H5, PaymentMethod.NATIVE]
    
    async def create_payment(self, request: PaymentRequest) -> PaymentResponse:
        """创建微信支付订单"""
        try:
            # 记录订单创建日志
            payment_logger.log_order_create(
                request.order_id, 
                request.user_id, 
                "wechat_pay", 
                float(request.amount),
                {"method": request.payment_method.value, "subject": request.subject}
            )
            
            # 构建统一下单参数
            params = {
                "appid": self.app_id,
                "mch_id": self.mch_id,
                "nonce_str": self._generate_nonce_str(),
                "body": request.subject,
                "out_trade_no": request.order_id,
                "total_fee": int(request.amount * 100),  # 微信支付金额单位为分
                "spbill_create_ip": "127.0.0.1",  # 客户端IP
                "notify_url": request.notify_url or self.notify_url,
                "trade_type": self._get_trade_type(request.payment_method),
            }
            
            # 添加特定支付方式的参数
            if request.payment_method == PaymentMethod.JSAPI:
                # 公众号支付需要openid
                if "openid" not in request.extra_params:
                    payment_logger.log_service_error(
                        "wechat_pay", 
                        ValueError("JSAPI支付需要提供openid"),
                        request.order_id,
                        {"method": "JSAPI", "missing_param": "openid"}
                    )
                    return PaymentResponse(
                        success=False,
                        error_message="JSAPI支付需要提供openid"
                    )
                params["openid"] = request.extra_params["openid"]
            elif request.payment_method == PaymentMethod.H5:
                # H5支付需要场景信息
                params["scene_info"] = json.dumps({
                    "h5_info": {
                        "type": "Wap",
                        "wap_url": "https://thrush-ai.com",
                        "wap_name": "画眉AI"
                    }
                })
            
            # 生成签名
            params["sign"] = self._generate_sign(params)
            
            # 调用微信支付API
            xml_data = self._dict_to_xml(params)
            
            # 记录网络请求日志
            payment_logger.log_network_request(
                "wechat_pay", 
                self.unifiedorder_url, 
                "POST", 
                {"Content-Type": "application/xml"}, 
                params
            )
            
            async with aiohttp.ClientSession() as session:
                async with session.post(
                    self.unifiedorder_url,
                    data=xml_data,
                    headers={"Content-Type": "application/xml"}
                ) as response:
                    result_xml = await response.text()
                    result = self._xml_to_dict(result_xml)
                    
                    # 记录网络响应日志
                    payment_logger.log_network_response(
                        "wechat_pay", 
                        self.unifiedorder_url, 
                        response.status, 
                        result
                    )
                    
                    if result.get("return_code") == "SUCCESS" and result.get("result_code") == "SUCCESS":
                        payment_logger.log_payment_event(
                            PaymentEventType.ORDER_CREATE,
                            PaymentLogLevel.INFO,
                            "微信支付订单创建成功",
                            order_id=request.order_id,
                            provider="wechat_pay",
                            extra_data={"prepay_id": result.get("prepay_id")}
                        )
                        return self._handle_payment_success(request, result)
                    else:
                        error_msg = result.get("return_msg") or result.get("err_code_des", "支付创建失败")
                        payment_logger.log_service_error(
                            "wechat_pay",
                            Exception(error_msg),
                            request.order_id,
                            {
                                "return_code": result.get("return_code"),
                                "result_code": result.get("result_code"),
                                "err_code": result.get("err_code")
                            }
                        )
                        return PaymentResponse(
                            success=False,
                            error_message=error_msg
                        )
        
        except Exception as e:
            payment_logger.log_service_error("wechat_pay", e, request.order_id if 'request' in locals() else None)
            return PaymentResponse(
                success=False,
                error_message=f"微信支付创建失败: {str(e)}"
            )
    
    def _handle_payment_success(self, request: PaymentRequest, result: Dict[str, Any]) -> PaymentResponse:
        """处理支付创建成功的响应"""
        prepay_id = result.get("prepay_id")
        
        if request.payment_method == PaymentMethod.H5:
            # H5支付返回支付URL
            return PaymentResponse(
                success=True,
                order_id=request.order_id,
                payment_url=result.get("mweb_url"),
                provider_order_id=prepay_id
            )
        
        elif request.payment_method == PaymentMethod.NATIVE:
            # 扫码支付返回二维码链接
            return PaymentResponse(
                success=True,
                order_id=request.order_id,
                qr_code=result.get("code_url"),
                provider_order_id=prepay_id
            )
        
        elif request.payment_method == PaymentMethod.APP:
            # APP支付返回调起支付所需的参数
            app_pay_data = {
                "appid": self.app_id,
                "partnerid": self.mch_id,
                "prepayid": prepay_id,
                "package": "Sign=WXPay",
                "noncestr": self._generate_nonce_str(),
                "timestamp": str(int(time.time()))
            }
            app_pay_data["sign"] = self._generate_sign(app_pay_data)
            
            return PaymentResponse(
                success=True,
                order_id=request.order_id,
                payment_data=app_pay_data,
                provider_order_id=prepay_id
            )
        
        elif request.payment_method == PaymentMethod.JSAPI:
            # 公众号支付返回调起支付所需的参数
            jsapi_data = {
                "appId": self.app_id,
                "timeStamp": str(int(time.time())),
                "nonceStr": self._generate_nonce_str(),
                "package": f"prepay_id={prepay_id}",
                "signType": "MD5"
            }
            jsapi_data["paySign"] = self._generate_sign(jsapi_data)
            
            return PaymentResponse(
                success=True,
                order_id=request.order_id,
                payment_data=jsapi_data,
                provider_order_id=prepay_id
            )
        
        return PaymentResponse(
            success=True,
            order_id=request.order_id,
            provider_order_id=prepay_id
        )
    
    async def query_payment(self, order_id: str) -> Optional[PaymentNotification]:
        """查询微信支付状态"""
        try:
            params = {
                "appid": self.app_id,
                "mch_id": self.mch_id,
                "out_trade_no": order_id,
                "nonce_str": self._generate_nonce_str(),
            }
            params["sign"] = self._generate_sign(params)
            
            xml_data = self._dict_to_xml(params)
            
            async with aiohttp.ClientSession() as session:
                async with session.post(
                    self.orderquery_url,
                    data=xml_data,
                    headers={"Content-Type": "application/xml"}
                ) as response:
                    result_xml = await response.text()
                    result = self._xml_to_dict(result_xml)
                    
                    if result.get("return_code") == "SUCCESS" and result.get("result_code") == "SUCCESS":
                        trade_state = result.get("trade_state")
                        
                        if trade_state == "SUCCESS":
                            status = OrderStatus.PAID
                        elif trade_state in ["REFUND", "REVOKED"]:
                            status = OrderStatus.REFUNDED
                        elif trade_state in ["CLOSED", "PAYERROR"]:
                            status = OrderStatus.FAILED
                        else:
                            status = OrderStatus.PENDING
                        
                        return PaymentNotification(
                            order_id=order_id,
                            provider_order_id=result.get("transaction_id", ""),
                            status=status,
                            amount=Decimal(result.get("total_fee", 0)) / 100,
                            currency="CNY",
                            transaction_id=result.get("transaction_id"),
                            raw_data=result
                        )
            
            return None
        
        except Exception as e:
            print(f"查询微信支付状态失败: {e}")
            return None
    
    async def handle_notification(self, raw_data: Dict[str, Any]) -> Optional[PaymentNotification]:
        """处理微信支付通知"""
        try:
            # 记录收到回调通知
            payment_logger.log_callback_received("wechat_pay", raw_data)
            
            # 验证签名
            sign = raw_data.pop("sign", "")
            signature_valid = self.verify_signature(raw_data, sign)
            
            # 记录签名验证结果
            payment_logger.log_signature_verify(
                "wechat_pay",
                raw_data.get("out_trade_no", "unknown"),
                signature_valid,
                None if signature_valid else "签名验证失败"
            )
            
            if not signature_valid:
                return None
            
            if raw_data.get("return_code") == "SUCCESS" and raw_data.get("result_code") == "SUCCESS":
                notification = PaymentNotification(
                    order_id=raw_data.get("out_trade_no"),
                    provider_order_id=raw_data.get("transaction_id"),
                    status=OrderStatus.PAID,
                    amount=Decimal(raw_data.get("total_fee", 0)) / 100,
                    currency="CNY",
                    transaction_id=raw_data.get("transaction_id"),
                    raw_data=raw_data
                )
                
                # 记录成功的支付通知
                payment_logger.log_payment_event(
                    PaymentEventType.ORDER_CALLBACK,
                    PaymentLogLevel.INFO,
                    "微信支付成功回调处理完成",
                    order_id=notification.order_id,
                    provider="wechat_pay",
                    amount=float(notification.amount),
                    extra_data={"transaction_id": notification.transaction_id}
                )
                
                return notification
            else:
                # 记录失败的支付通知
                payment_logger.log_service_error(
                    "wechat_pay",
                    Exception(f"支付失败 - {raw_data.get('err_code_des', '未知错误')}"),
                    raw_data.get("out_trade_no"),
                    {
                        "return_code": raw_data.get("return_code"),
                        "result_code": raw_data.get("result_code"),
                        "err_code": raw_data.get("err_code")
                    }
                )
            
            return None
        
        except Exception as e:
            payment_logger.log_service_error("wechat_pay", e, raw_data.get("out_trade_no") if raw_data else None)
            return None
    
    async def refund_payment(self, order_id: str, amount: Decimal, reason: str = None) -> bool:
        """微信支付退款"""
        try:
            params = {
                "appid": self.app_id,
                "mch_id": self.mch_id,
                "nonce_str": self._generate_nonce_str(),
                "out_trade_no": order_id,
                "out_refund_no": f"refund_{order_id}_{int(time.time())}",
                "total_fee": int(amount * 100),
                "refund_fee": int(amount * 100),
                "refund_desc": reason or "用户申请退款"
            }
            params["sign"] = self._generate_sign(params)
            
            xml_data = self._dict_to_xml(params)
            
            # 退款接口需要使用证书
            async with aiohttp.ClientSession() as session:
                async with session.post(
                    self.refund_url,
                    data=xml_data,
                    headers={"Content-Type": "application/xml"},
                    # TODO: 添加客户端证书配置
                ) as response:
                    result_xml = await response.text()
                    result = self._xml_to_dict(result_xml)
                    
                    return (result.get("return_code") == "SUCCESS" and 
                           result.get("result_code") == "SUCCESS")
        
        except Exception as e:
            print(f"微信支付退款失败: {e}")
            return False
    
    def verify_signature(self, data: Dict[str, Any], signature: str) -> bool:
        """验证微信支付签名"""
        expected_sign = self._generate_sign(data)
        return expected_sign == signature
    
    def _generate_sign(self, params: Dict[str, Any]) -> str:
        """生成微信支付签名"""
        # 过滤空值并排序
        filtered_params = {k: v for k, v in params.items() if v != "" and k != "sign"}
        sorted_params = sorted(filtered_params.items())
        
        # 拼接字符串
        sign_str = "&".join([f"{k}={v}" for k, v in sorted_params])
        sign_str += f"&key={self.api_key}"
        
        # MD5加密并转为大写
        return hashlib.md5(sign_str.encode("utf-8")).hexdigest().upper()
    
    def _generate_nonce_str(self) -> str:
        """生成随机字符串"""
        return str(uuid.uuid4()).replace("-", "")
    
    def _get_trade_type(self, payment_method: PaymentMethod) -> str:
        """获取微信支付交易类型"""
        mapping = {
            PaymentMethod.APP: "APP",
            PaymentMethod.JSAPI: "JSAPI",
            PaymentMethod.H5: "MWEB",
            PaymentMethod.NATIVE: "NATIVE"
        }
        return mapping.get(payment_method, "NATIVE")
    
    def _dict_to_xml(self, data: Dict[str, Any]) -> str:
        """字典转XML"""
        xml = "<xml>"
        for key, value in data.items():
            xml += f"<{key}>{value}</{key}>"
        xml += "</xml>"
        return xml
    
    def _xml_to_dict(self, xml_str: str) -> Dict[str, Any]:
        """XML转字典（简单实现）"""
        import xml.etree.ElementTree as ET
        root = ET.fromstring(xml_str)
        result = {}
        for child in root:
            result[child.tag] = child.text
        return result